package com.lujieni.nio.c2;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;

public class SelectorServer {

    public static void main(String[] args) throws IOException {

        // 1.初始化selector
        Selector selector = Selector.open();

        ServerSocketChannel server = ServerSocketChannel.open();
        server.bind(new InetSocketAddress(8080)); // 监听8080端口
        server.configureBlocking(false); // 必须要加否则报错

        // 2.和selector建立绑定并指定监听的事件
        SelectionKey sscKey = server.register(selector, 0, null);// 注册到selector中并且不监听任何事件
        sscKey.interestOps(SelectionKey.OP_ACCEPT);


        while (true){
            // 3.阻塞等到消费事件
            selector.select(); // 阻塞

            // 4.处理事件
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while(iterator.hasNext()){
                SelectionKey selectionKey = iterator.next();
                if(selectionKey.isAcceptable()){
                    // 处理连接事件
                    ServerSocketChannel channel = (ServerSocketChannel)selectionKey.channel();
                    SocketChannel sc = channel.accept();
                    sc.configureBlocking(false); // 都是非阻塞不然会报错
                    ByteBuffer byteBuffer = ByteBuffer.allocate(16); // 每个channel都有各自专属的ByteBuffer
                    SelectionKey sk = sc.register(selector, 0, byteBuffer);
                    sk.interestOps(SelectionKey.OP_READ); // 监听读事件
                }else if(selectionKey.isReadable()){
                    // 客户端结束连接也会触发读事件
                    try {
                        System.out.println("readable event");
                        SocketChannel sc = (SocketChannel)selectionKey.channel();
                        ByteBuffer attachment = (ByteBuffer)selectionKey.attachment(); // 用专属的attachment来读取
                        int read = sc.read(attachment);
                        if(read == - 1){
                            selectionKey.cancel(); // 如果正常断开, read方法的返回值是-1
                        }else{
                            split(attachment, selectionKey);
                        }
                    } catch (IOException e) {
                        selectionKey.cancel(); // 客户端进程结束了关闭连接也会触发读事件
                    }
                }
                iterator.remove(); // 消费完后必须移除 selector不会帮我们手动移除
            }
        }
    }


    public static void split(ByteBuffer source, SelectionKey sk){
        boolean flag = false;
        /**
         * 1.循环遍历是否包含\n
         * 2.如果有就将\n之前的截取放入一个新的ByteBuffer中打印输出
         * 3.循环遍历知道结束
         *
         */
        source.flip(); // 读模式
        for (int i = 0; i < source.limit() ; i++) {
            if(source.get(i) == '\n'){
                flag = true;
                int length = i - source.position();
                ByteBuffer temp = ByteBuffer.allocate(length);
                for (int j = 0; j < length ; j++) { // j < length 细节
                    temp.put(source.get());
                }
                temp.flip();
                System.out.println(StandardCharsets.UTF_8.decode(temp));
            }
        }
        if(!flag){
            // 说明并没有'\n'存在需要扩容
            ByteBuffer newByteBuffer = ByteBuffer.allocate(source.capacity() << 1);// 左移一位
            newByteBuffer.put(source);
            sk.attach(newByteBuffer);
        }else{
            source.compact(); // 写模式
        }
    }

}
